package com.eastfair.gateway.filters;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import com.alibaba.fastjson.JSONObject;
import com.eastfair.core.base.R;
import com.eastfair.core.context.ContextConstants;
import com.eastfair.core.context.ContextUtil;
import com.eastfair.core.exception.BizException;
import com.eastfair.core.utils.HumpUtil;
import com.eastfair.gateway.dto.BackListDTO;
import com.eastfair.gateway.entity.BackList;
import com.eastfair.gateway.service.BackListService;
import com.eastfair.gateway.service.GatewayService;
import com.eastfair.handler.EfTokenHandler;
import com.eastfair.jwt.model.AuthInfo;
import com.eastfair.jwt.service.JwtService;
import com.eastfair.properties.IgnoreProperties;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * 鉴权过滤器
 *
 * @Author ligang
 * @Date 2020/3/16 23:33
 */
@Slf4j
@Component
public class VerifyFilter implements GlobalFilter, Ordered {
    @Autowired
    GatewayService gatewayService;
    /**
     * 设置webflux模型响应
     *
     * @param response    ServerHttpResponse
     * @param contentType content-type
     * @param status      http状态码
     * @param value       响应内容
     * @return Mono<Void>
     */
    private static Mono<Void> buildResponse(ServerHttpResponse response, String contentType,
                                            HttpStatus status, Object value) {
        response.setStatusCode(status);
        response.getHeaders().add(HttpHeaders.CONTENT_TYPE, contentType);
        DataBuffer dataBuffer = response.bufferFactory().wrap(JSONObject.toJSONString(value).getBytes());
        return response.writeWith(Mono.just(dataBuffer));
    }

    public Mono<Void> assembleExchange(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String requestUrl = request.getPath().toString();
        String requestMethod = request.getMethodValue();
        if (HttpMethod.POST.toString().equals(requestMethod) || HttpMethod.PUT.toString().equals(requestMethod)) {
            return DataBufferUtils.join(exchange.getRequest().getBody()).flatMap(dataBuffer -> {
                byte[] bytes = new byte[dataBuffer.readableByteCount()];
                dataBuffer.read(bytes);
                String postRequestBodyStr = new String(bytes, StandardCharsets.UTF_8);
                exchange.getAttributes().put("POST_BODY", postRequestBodyStr);
                DataBufferUtils.release(dataBuffer);
                Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
                    DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
                    return Mono.just(buffer);
                });
                // 下面的将请求体再次封装写回到request里，传到下一级，否则，由于请求体已被消费，后续的服务将取不到值
                ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
                    @Override
                    public Flux<DataBuffer> getBody() {
                        return cachedFlux;
                    }
                };
                // 封装request，传给下一级
                return chain.filter(exchange.mutate().request(mutatedRequest).build());
            });
        } else if (HttpMethod.GET.toString().equals(requestMethod)
                || HttpMethod.DELETE.toString().equals(requestMethod)) {
            MultiValueMap<String, String> getRequestParams = request.getQueryParams();
            log.debug("\n 请求url:`{}` \n 请求类型：{} \n 请求参数：{}", requestUrl, requestMethod, getRequestParams);
            return chain.filter(exchange);
        }
        return chain.filter(exchange);
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        ServerHttpRequest.Builder mutate = request.mutate();
        //用户id
        String userId = getHeader(ContextConstants.JWT_KEY_USER_ID, request);
        // 校验黑名单
//        if (verifyBackList(userId)) {
//            // 封装错误信息
//            Map<String, Object> responseData = Maps.newHashMap();
//            responseData.put("code", 403);
//            responseData.put("message", "账户在黑名单期间");
//            responseData.put("cause", "user is back");
//            // 输出错误信息到页面
//            return responseMsg(responseData, response);
//        }

        return assembleExchange(exchange, chain);
    }


    /**
     * 校验黑名单
     *
     * @param userId
     * @return 锁定时间戳
     */
    private boolean verifyBackList(String userId) {
        if (StringUtils.isNotBlank(userId)) {
            BackListDTO backListDTO = new BackListDTO();
            backListDTO.setUserId(Long.parseLong(userId));
            List<BackList> backListList = gatewayService.queryBackList(backListDTO);
            if (backListList == null || backListList.isEmpty()) {
                return false;
            }
            for (BackList backList : backListList) {
                if (backList.getLimitStart().isBefore(LocalDateTime.now()) && backList.getLimitEnd().isAfter(LocalDateTime.now())) {
                    //有效时间的数据
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 返回错误信息
     *
     * @param responseData
     * @param response
     * @return
     */
    private Mono<Void> responseMsg(Map<String, Object> responseData, ServerHttpResponse response) {
        try {
            // 将信息转换为 JSON
            ObjectMapper objectMapper = new ObjectMapper();
            byte[] data = objectMapper.writeValueAsBytes(responseData);
            // 输出错误信息到页面
            DataBuffer buffer = response.bufferFactory().wrap(data);
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
            return response.writeWith(Mono.just(buffer));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return null;
    }


    protected String getHeader(String headerName, ServerHttpRequest request) {
        HttpHeaders headers = request.getHeaders();
        String token = StrUtil.EMPTY;
        if (headers == null || headers.isEmpty()) {
            return token;
        }

        token = headers.getFirst(headerName);

        if (StrUtil.isNotBlank(token)) {
            return token;
        }

        return request.getQueryParams().getFirst(headerName);
    }


    /**
     * 设置过滤器的执行顺序
     *
     * @return
     */
    @Override
    public int getOrder() {
        return  -100;
    }

}
